package com.fy.login;

import com.fy.login.exceptions.UserNotExistsException;
import com.fy.login.internal.MetadataCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhufeifei 2022/8/9
 **/

public class LoginManager implements LoginConstants{

    private static final Map<Integer, LoginContextWrapper> LOGIN_MAP = new ConcurrentHashMap<>();
    private static final ThreadLocal<LoginContextWrapper> TL_LOGIN_CONTEXT = new ThreadLocal<>();

    public static LoginContextWrapper getLoginContext() {
        LoginContextWrapper loginContextWrapper = TL_LOGIN_CONTEXT.get();
        if (loginContextWrapper != null) {
            return loginContextWrapper;
        }
        throw new UserNotExistsException();
    }

    public static LoginContextWrapper getLoginContext(BaseMetadata metadata) {
        int hashCode = metadata.hashCode();
        LoginContextWrapper context = null;
        if (LOGIN_MAP.containsKey(hashCode)) {
            context = LOGIN_MAP.get(hashCode);
        }
        if (context != null){
            return context;
        }
        throw new UserNotExistsException();
    }

    public static void saveLoginContext(int hashKey, LoginContextWrapper context) {
        if (LOGIN_MAP.containsKey(hashKey)) {
            throw new RuntimeException("用户重复登陆");
        }
        LOGIN_MAP.put(hashKey, context);
    }

    public static LoginContextWrapper login(LoginType type, CallbackHandler callbackHandler) throws LoginException {

        BaseMetadata metadata = getMetadata(callbackHandler);

        LoginContext context = new LoginContext(metadata.getRole().toString().toLowerCase(Locale.ROOT), callbackHandler);

        context.login();

        LoginContextWrapper contextWrapper = new LoginContextWrapper(context, metadata, type);

        if (LoginType.LONG_TERM.equals(type)) {
            saveLoginContext(metadata.hashCode(), contextWrapper);
        } else {
            TL_LOGIN_CONTEXT.set(contextWrapper);
        }

        return contextWrapper;
    }


    public static void logout(BaseMetadata metadata) throws LoginException{
        int hashKey = metadata.hashCode();
        if ( !LOGIN_MAP.containsKey(hashKey)) {
            throw new LoginException(NON_USER_DATA);
        }
        LoginContextWrapper wrapper = LOGIN_MAP.get(hashKey);
        logout(wrapper.getContext());
        LOGIN_MAP.remove(hashKey);
    }

    public static void logout() throws LoginException {
        LoginContextWrapper loginContextWrapper = TL_LOGIN_CONTEXT.get();
        if (loginContextWrapper == null) {
            throw new LoginException(NON_USER_DATA);
        }
        logout(loginContextWrapper.getContext());
        TL_LOGIN_CONTEXT.remove();
    }

    public static void logout(LoginType type, BaseMetadata metadata) throws LoginException {
        if (LoginType.LONG_TERM.equals(type)) {
            logout(Objects.requireNonNull(metadata));
            return;
        }
        logout();
    }


    public static void logout(LoginContext loginContext) throws LoginException {
        loginContext.logout();
    }



    private static BaseMetadata getMetadata(CallbackHandler handler) throws LoginException {
        try {
            MetadataCallback<?> metadataCallback = new MetadataCallback<>();
            handler.handle(new Callback[] { metadataCallback});
            return metadataCallback.getMetadata();
        } catch (IOException | UnsupportedCallbackException e) {
            throw new LoginException(e.getMessage());
        }
    }

}
